import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;

import model.EErrorPopUp;
import model.NoSuchDirectoryException;
import model.cnf.CNF;
import model.cnf.FileCNF;
import model.cnf.Stat;
import model.preprocessor.PreProcessorFactory;
import model.session.CNFUse;
import model.session.ExecutableSessionBlock;
import model.session.ExecutionStatus;
import model.session.PreprocessorExecutionBlock;
import model.session.SessionBlock;
import model.session.SolverExecutionBlock;
import model.solver.SolverFactory;

import org.junit.Before;
import org.junit.Test;

import vue.IIGSessionManager;
import controller.ISessionManager;
import controller.SessionManager;
import controller.preprocessor.PreProcessor;
import controller.solver.Solver;

/**
 * 
 */

/**
 * @author GRONOWSKI Stephane svan001@aol.com
 * 
 *         igsat 2010
 * 
 */
public class TestSessionManager {

	private String				cnflonguepath	= "./tstcnf/longue.cnf";
	private String				cnfsatpath		= "./tstcnf/sat.cnf";
	private String				cnfunsatpath	= "./tstcnf/unsat.cnf";
	private String				cnfcourpath		= "./tstcnf/cour.cnf";
	private String				cnfcourtepath	= "./tstcnf/courte.cnf";
	private String				out				= "./cnf/newcnf.cnf";
	private String				out2			= "./cnf/newcnf2.cnf";


	private File				dirpreproc		= new File("./preproc");
	private File				dirsolveur		= new File("./solveur");

	private PreProcessorFactory	preprocfactory	= new PreProcessorFactory(dirpreproc);
	private SolverFactory		solverfactory	= new SolverFactory(dirsolveur);

	TestIGSessionManager		ig;
	SessionManager				man;

	SolverFactory				sfac			= new SolverFactory(dirsolveur);
	PreProcessorFactory			pfac			= new PreProcessorFactory(dirpreproc);

	CNFUse						defaultCNFUse;

	Semaphore					sem;

	private CNFUse getCNFUse(String path) {
		CNFUse cnfuse;
		CNF cnf;

		try
		{
			cnf = new FileCNF(path);
		} catch (IOException e)
		{
			e.printStackTrace();
			return null;
		}

		cnfuse = new CNFUse(cnf);
		return cnfuse;
	}

	/**
	 * reinitialize the global attribute with the defaultCNFUse
	 */
	private void newSet() {
		sem = new Semaphore(0);
		ig = new TestIGSessionManager(sem);
		man = new SessionManager(defaultCNFUse, sfac, pfac);
		man.setIIGSessionManager(ig);
		ig.setSessionManager(man);

	}

	@Before
	public void setUp() {
		defaultCNFUse = getCNFUse(cnfcourtepath);
		newSet();
	}

	@Test
	public void testCheckCNFStat() {

		System.out.println("\n\nTEST : testCheckCNFStat\n");

		defaultCNFUse.getCnf().setStat(null);

		TestIGSessionManager ig2 = new TestIGSessionManager(sem) {

			@Override
			public void updateCNFInformation(CNFUse cnf) {
				System.out.println("update recu");
				attend.release();
			}

			@Override
			public void showErrorPopup(EErrorPopUp type, String message) {
				attend.release();
				System.out.println(type + message);
				fail("problem checking CNF");
			}
		};

		man.setIIGSessionManager(ig2);
		ig2.setSessionManager(man);
		// Semaphore sem = new Semaphore(1);

		man.checkCNFStat(defaultCNFUse);

		try
		{
			sem.acquire();
		} catch (InterruptedException e)
		{
			e.printStackTrace();
			fail("problem when acquire sem");
		}

		Stat stat = defaultCNFUse.getCnf().getStat();
		assertNotNull("doesn't get the stat", stat);

		try
		{
			defaultCNFUse.getCnf().checkStat();
		} catch (IOException e)
		{
			e.printStackTrace();
			fail("problem checking the stat");
		}
		assertTrue("doesn't get the stat correctly", defaultCNFUse.getCnf().getStat().equals(stat));
	}

	@Test
	public void testSimpleClose() {
		Map<SolverExecutionBlock, CNFUse> sols = man.getSolverExec();// new
		// HashMap<SolverExecutionBlock,
		// CNFUse>();
		Map<PreprocessorExecutionBlock, CNFUse> precs = man.getPreprocExec();// new
		// HashMap<PreprocessorExecutionBlock,
		// CNFUse>();

		try
		{
			// rempli
			sols.put(new SolverExecutionBlock(defaultCNFUse, ExecutionStatus.RUNNING, sfac.createMiniSatSolver(), null), defaultCNFUse);
			sols.put(new SolverExecutionBlock(defaultCNFUse, ExecutionStatus.RUNNING, sfac.createRSatSolver(), null), defaultCNFUse);

			precs.put(new PreprocessorExecutionBlock(defaultCNFUse, ExecutionStatus.RUNNING, pfac.createRevivalPreProcessor(), null),
					defaultCNFUse);
			precs.put(new PreprocessorExecutionBlock(defaultCNFUse, ExecutionStatus.RUNNING, pfac.createSatellitePreProcessor(), null),
					defaultCNFUse);
			precs.put(new PreprocessorExecutionBlock(defaultCNFUse, ExecutionStatus.RUNNING, pfac.createHyprePreProcessor(), null),
					defaultCNFUse);

		} catch (NoSuchDirectoryException e)
		{
			e.printStackTrace();
			fail("error creating solver/preproc");
		}

		assertTrue("session doesn't close correctly", man.close());

		for (PreprocessorExecutionBlock p : precs.keySet())
			assertSame("preproc block isn't finished", ExecutionStatus.FINISH, p.getStatus());

		for (SolverExecutionBlock p : sols.keySet())
			assertSame("solver block isn't finished", ExecutionStatus.FINISH, p.getStatus());
	}

	/**
	 * pretty ugly don't do this at home ;)
	 */
	@Test
	public void testComplicateClose() {
		defaultCNFUse = getCNFUse(cnfcourtepath);
		newSet();

		Semaphore attend = new Semaphore(0);
		Map<SolverExecutionBlock, CNFUse> sols = man.getSolverExec();
		Map<PreprocessorExecutionBlock, CNFUse> precs = man.getPreprocExec();

		final PreProcessor preproc;
		try
		{
			// rempli
			sols.put(new SolverExecutionBlock(defaultCNFUse, ExecutionStatus.RUNNING, sfac.createMiniSatSolver(), null), defaultCNFUse);
			sols.put(new SolverExecutionBlock(defaultCNFUse, ExecutionStatus.RUNNING, sfac.createRSatSolver(), null), defaultCNFUse);

			precs.put(new PreprocessorExecutionBlock(defaultCNFUse, ExecutionStatus.RUNNING, pfac.createRevivalPreProcessor(), null),
					defaultCNFUse);
			precs.put(new PreprocessorExecutionBlock(defaultCNFUse, ExecutionStatus.RUNNING, pfac.createHyprePreProcessor(), null),
					defaultCNFUse);

			preproc = pfac.createRevivalPreProcessor();

		} catch (NoSuchDirectoryException e)
		{
			e.printStackTrace();
			fail("error creating solver/preproc");
			return;
		}

		TestIGSessionManager ig2 = new TestIGSessionManager(attend) {

			@Override
			public void updateCNFUse(CNFUse cnf) {
				attend.release();
			}

			@Override
			public void addRunningExecution(ExecutableSessionBlock exec, CNFUse mere) {
				System.out.println("lancer sur l'ig");
			}
		};

		man.setIIGSessionManager(ig2);
		ig2.setSessionManager(man);

		preproc.getPreProcessorParameter().setOut(out);
		man.executePreProcessor(preproc, defaultCNFUse);

		System.out.println("dort");
		try
		{
			attend.acquire();
		} catch (Exception e)
		{
			e.printStackTrace();
			fail("error using semaphore");
		}
		System.out.println("reveiller");

		System.out.println("update recu");

		CNFUse res = (CNFUse) defaultCNFUse.getPreproc().values().toArray()[0];
		Solver solver = null;
		// Map<SolverExecutionBlock, CNFUse> sols = man.getSolverExec();
		// Map<PreprocessorExecutionBlock, CNFUse> precs = man.getPreprocExec();

		try
		{
			sols.put(new SolverExecutionBlock(res, ExecutionStatus.RUNNING, sfac.createMiniSatSolver(), null), defaultCNFUse);
			precs.put(new PreprocessorExecutionBlock(res, ExecutionStatus.RUNNING, pfac.createRevivalPreProcessor(), null), defaultCNFUse);
			solver = sfac.createMiniSatSolver();
		} catch (NoSuchDirectoryException e)
		{
			e.printStackTrace();
			fail("creating solver/preproc");
		}

		man.executeSolver(solver, res);

		System.out.println("ferme");
		assertTrue("session doesn't close correctly", man.close());

		for (PreprocessorExecutionBlock p : precs.keySet())
			assertSame("preproc block isn't finished", ExecutionStatus.FINISH, p.getStatus());

		for (SolverExecutionBlock p : sols.keySet())
			assertSame("solver block isn't finished", ExecutionStatus.FINISH, p.getStatus());

	}

	@Test
	public void testDoesntExecutePreprocessorOutSession() {
		defaultCNFUse = getCNFUse(cnfunsatpath);
		newSet();

		CNFUse tmp = getCNFUse(cnfsatpath);

		try
		{
			man.executePreProcessor(pfac.createRevivalPreProcessor(), tmp);
		} catch (NoSuchDirectoryException e)
		{
			e.printStackTrace();
			fail("fail creating preprocessor");
		} catch (IllegalArgumentException e)
		{// bien
			return;
		}

		fail("session manager accept preprocessor execution it is not suppose to execute");

	}

	@Test
	public void testDoesntExecuteSolverOutSession() {
		defaultCNFUse = getCNFUse(cnfunsatpath);
		newSet();

		CNFUse tmp = getCNFUse(cnfsatpath);

		try
		{
			man.executeSolver(sfac.createMiniSatSolver(), tmp);
		} catch (NoSuchDirectoryException e)
		{
			e.printStackTrace();
			fail("fail creating preprocessor");
		} catch (IllegalArgumentException e)
		{// bien
			return;
		}

		fail("session manager accept solver execution it is not suppose to execute");

	}


	@Test
	public void testPreprocessorExecution() {

		System.out.println("\n\nTEST : testPreprocessorExecution\n");

		defaultCNFUse = getCNFUse(cnfcourtepath);
		newSet();

		Map<SolverExecutionBlock, CNFUse> sols = man.getSolverExec();
		Map<PreprocessorExecutionBlock, CNFUse> precs = man.getPreprocExec();

		PreProcessor preproc = null;
		try
		{
			preproc = pfac.createRevivalPreProcessor();
		} catch (NoSuchDirectoryException e)
		{
			e.printStackTrace();
			fail("error creating preproc");
		}


		TestIGSessionManager ig2 = new TestIGSessionManager(sem) {
			@Override
			public void updateCNFUse(CNFUse cnf) {
				attend.release();
			}

			@Override
			public void addRunningExecution(ExecutableSessionBlock exec, CNFUse mere) {
				System.out.println("lancer sur l'ig");
			}
		};

		man.setIIGSessionManager(ig2);
		ig2.setSessionManager(man);

		// execute
		preproc.getPreProcessorParameter().setOut(out);
		man.executePreProcessor(preproc, defaultCNFUse);
		System.out.println("dort");
		try
		{
			sem.acquire();
		} catch (Exception e)
		{
			e.printStackTrace();
			fail("fail acquiring sem");
		}
		System.out.println("reveiller");

		CNFUse res = null;
		try
		{
			res = (CNFUse) defaultCNFUse.getPreproc().values().toArray()[0];
		} catch (Exception e)
		{
			e.printStackTrace();
			fail("fail get the CNFUse result of a preprocessor execution");
		}
		assertNotNull("don't store the preprocesor CNFUse result in the original CNFUse ", res);

		PreProcessor preproc2 = null;
		try
		{
			preproc2 = pfac.createRevivalPreProcessor();
		} catch (NoSuchDirectoryException e)
		{
			e.printStackTrace();
			fail("creating preproc");
		}

		// execute
		preproc2.getPreProcessorParameter().setOut(out2);
		man.executePreProcessor(preproc, res);
		System.out.println("dort");
		try
		{
			sem.acquire();
		} catch (Exception e)
		{
			e.printStackTrace();
			fail("fail acquiring sem");
		}
		System.out.println("reveiller");

		CNFUse res2 = null;
		try
		{
			res2 = (CNFUse) res.getPreproc().values().toArray()[0];
		} catch (Exception e)
		{
			e.printStackTrace();
			fail("fail get the CNFUse result of a preprocessor execution");
		}
		assertNotNull("don't store the preprocesor CNFUse result in the original CNFUse ", res2);


		assertTrue("preproc blocks aren't consider finish", precs.isEmpty());

		assertTrue("solver block aren't consider finish", sols.isEmpty());
	}


	@Test
	public void testSolverExecution() {

		System.out.println("\n\nTEST : testSolverExecution\n");

		defaultCNFUse = getCNFUse(cnfsatpath);
		newSet();

		Map<SolverExecutionBlock, CNFUse> sols = man.getSolverExec();
		Map<PreprocessorExecutionBlock, CNFUse> precs = man.getPreprocExec();

		Solver solver = null;
		try
		{
			solver = sfac.createMiniSatSolver();
		} catch (NoSuchDirectoryException e)
		{
			e.printStackTrace();
			fail("error creating solver");
		}

		TestIGSessionManager ig2 = new TestIGSessionManager(sem) {
			@Override
			public void updateCNFUse(CNFUse cnf) {
				attend.release();
			}

			@Override
			public void addRunningExecution(ExecutableSessionBlock exec, CNFUse mere) {
				System.out.println("lancer sur l'ig");
			}
		};

		man.setIIGSessionManager(ig2);
		ig2.setSessionManager(man);

		// execute
		// preproc.getPreProcessorParameter().setOut(out);
		man.executeSolver(solver, defaultCNFUse);
		System.out.println("dort");
		try
		{
			sem.acquire();
		} catch (Exception e)
		{
			e.printStackTrace();
			fail("fail acquiring sem");
		}
		System.out.println("reveiller");

		assertFalse("don't store the solver result correctly in the original CNFUse ", defaultCNFUse.getSolver().isEmpty());

		Solver solver2 = null;
		try
		{
			solver2 = sfac.createMiniSatSolver();
		} catch (NoSuchDirectoryException e)
		{
			e.printStackTrace();
			fail("error creating solver");
		}

		// execute
		// preproc2.getPreProcessorParameter().setOut(out2);
		man.executeSolver(solver2, defaultCNFUse);
		System.out.println("dort");
		try
		{
			sem.acquire();
		} catch (Exception e)
		{
			e.printStackTrace();
			fail("fail acquiring sem");
		}
		System.out.println("reveiller");

		defaultCNFUse.getSolver().remove(solver);
		assertFalse("don't store the solver result correctly in the original CNFUse ", defaultCNFUse.getSolver().isEmpty());

		assertTrue("preproc blocks aren't consider finish", precs.isEmpty());

		assertTrue("solver block aren't consider finish", sols.isEmpty());
	}

	@Test
	public void testSolverAndPreprocessorExecution() {

		System.out.println("\n\nTEST : testSolverAndPreprocessorExecution\n");

		defaultCNFUse = getCNFUse(cnfsatpath);
		newSet();

		Map<SolverExecutionBlock, CNFUse> sols = man.getSolverExec();
		Map<PreprocessorExecutionBlock, CNFUse> precs = man.getPreprocExec();

		PreProcessor preproc = null;
		try
		{
			preproc = pfac.createRevivalPreProcessor();
		} catch (NoSuchDirectoryException e)
		{
			e.printStackTrace();
			fail("error creating preproc");
		}

		TestIGSessionManager ig2 = new TestIGSessionManager(sem) {
			@Override
			public void updateCNFUse(CNFUse cnf) {
				attend.release();
			}

			@Override
			public void addRunningExecution(ExecutableSessionBlock exec, CNFUse mere) {
				System.out.println("lancer sur l'ig");
			}
		};

		man.setIIGSessionManager(ig2);
		ig2.setSessionManager(man);

		// execute
		preproc.getPreProcessorParameter().setOut(out);
		man.executePreProcessor(preproc, defaultCNFUse);
		System.out.println("dort");
		try
		{
			sem.acquire();
		} catch (Exception e)
		{
			e.printStackTrace();
			fail("fail acquiring sem");
		}
		System.out.println("reveiller");

		CNFUse res = null;
		try
		{
			res = (CNFUse) defaultCNFUse.getPreproc().values().toArray()[0];
		} catch (Exception e)
		{
			e.printStackTrace();
			fail("fail get the CNFUse result of a preprocessor execution");
		}
		assertNotNull("don't store the preprocesor CNFUse result in the original CNFUse ", res);

		Solver solver = null;
		try
		{
			solver = sfac.createMiniSatSolver();
		} catch (NoSuchDirectoryException e)
		{
			e.printStackTrace();
			fail("error creating solver");
		}

		// execute
		// preproc2.getPreProcessorParameter().setOut(out2);
		man.executeSolver(solver, defaultCNFUse);
		System.out.println("dort");
		try
		{
			sem.acquire();
		} catch (Exception e)
		{
			e.printStackTrace();
			fail("fail acquiring sem");
		}
		System.out.println("reveiller");

		assertFalse("don't store the solver result correctly in the original CNFUse ", defaultCNFUse.getSolver().isEmpty());

		Solver solver2 = null;
		try
		{
			solver2 = sfac.createMiniSatSolver();
		} catch (NoSuchDirectoryException e)
		{
			e.printStackTrace();
			fail("error creating solver");
		}

		// execute
		// preproc2.getPreProcessorParameter().setOut(out2);
		man.executeSolver(solver2, res);
		System.out.println("dort");
		try
		{
			sem.acquire();
		} catch (Exception e)
		{
			e.printStackTrace();
			fail("fail acquiring sem");
		}
		System.out.println("reveiller");

		assertFalse("don't store the solver result correctly in the original CNFUse ", res.getSolver().isEmpty());

		assertTrue("preproc blocks aren't consider finish", precs.isEmpty());
		assertTrue("solver block aren't consider finish", sols.isEmpty());
	}


	private class TestIGSessionManager implements IIGSessionManager {

		public List<EErrorPopUp>	popups		= new ArrayList<EErrorPopUp>();
		public List<SessionBlock>	running		= new ArrayList<SessionBlock>();
		public ISessionManager		manager		= null;
		public List<CNFUse>			showall		= new ArrayList<CNFUse>();
		public List<CNFUse>			updateinfo	= new ArrayList<CNFUse>();
		public List<CNFUse>			update		= new ArrayList<CNFUse>();
		public Semaphore			attend;

		public TestIGSessionManager() {

		}

		public void setAttend(Semaphore sem) {
			attend = sem;
		}

		public TestIGSessionManager(Semaphore sem) {
			attend = sem;
		}

		@Override
		public void addRunningExecution(ExecutableSessionBlock exec, CNFUse mere) {
			running.add(exec);
		}

		@Override
		public void removeRunningExecution(ExecutableSessionBlock exec, CNFUse mere) {
			running.remove(exec);
		}


		public void setSessionManager(ISessionManager sessionManager) {
			manager = sessionManager;
		}


		@Override
		public void showErrorPopup(EErrorPopUp type, String message) {
			popups.add(type);
		}

		@Override
		public void updateCNFInformation(CNFUse cnf) {
			updateinfo.add(cnf);
		}

		@Override
		public void updateCNFUse(CNFUse cnf) {
			update.add(cnf);
		}

		@Override
		public ISessionManager getSessionManager() {
			return manager;
		}

	}

}
